<template>
  <!-- 工作流 -->
  <div
    class="w-full h-full"
    id="main-canvas"
  >
    <VueFlow
      v-model="elements"
      :node-types="nodeTypes"
      :edge-types="edgeTypes"
      @node-drag-stop="handleNodeDragStop"
      :deleteKeyCode="['Delete', 'Backspace']"
    >
      <Controls />
      <Background />
      <template #edge-custom="props">
        <CustomEdge
          v-bind="props"
          :serviceaiid="id"
        />
      </template>
      <template #connection-line="props">
        <CustomEdge
          v-bind="props"
        />
      </template>
    </VueFlow>
  </div>
  <div class="flow-footer">
    <el-popover :width="420" trigger="click" ref="popRef" placement="top">
      <template #reference>
        <Button icon="md-add" type="primary">添加节点</Button>
      </template>
      <NodesPanelModule @node-click="handleAddNodes" @hide="handleHidePanel" />
    </el-popover>
  </div>
</template>
<script setup>
import { markRaw, ref } from 'vue';
import { VueFlow, useVueFlow } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import { Controls } from '@vue-flow/controls';
import { initFlow, initFlowDetail, deleteNode, createNode, editEdges, editPosition } from '@/api/modules/flow';
import { flowStore } from '@/store/flow';

import NodesPanelModule from './components/nodes-panel.vue';
import CustomEdge from './components/custom-edge.vue';
import StartNode from './components/nodes/start-node.vue';
import EndNode from './components/nodes/end-node.vue';
import InputNode from './components/nodes/input-node.vue';
import OutputNode from './components/nodes/output-node.vue';
import CodeNode from './components/nodes/code-node.vue';
import LLMNode from './components/nodes/LLM-node.vue';
import ConditionNode from './components/nodes/condition-node.vue';
import IntentNode from './components/nodes/intent-node.vue';
import LoopNode from './components/nodes/loop-node.vue';
import BatchNode from './components/nodes/batch-node.vue';
import VariableMergeNode from './components/nodes/variable-merge-node.vue';
import DiscussionNode from './components/nodes/discussion-node.vue';
import EditDscussionNode from './components/nodes/editdiscussion-node.vue';
import DelDscussionNode from './components/nodes/deldiscussion-node.vue';
import CheckDscussionNode from './components/nodes/checkdiscussion-node.vue';
import ContextNode from './components/nodes/context-node.vue';
import CheckContextNode from './components/nodes/checkcontext-node.vue';
import QueueNode from './components/nodes/queue-node.vue';
import CreateQueueNode from './components/nodes/createqueue-node.vue';
import EditQueueNode from './components/nodes/editqueue-node.vue';
import DelQueueNode from './components/nodes/delqueue-node.vue';
import AssignmentNode from './components/nodes/assignment-node.vue';
import MemorytNode from './components/nodes/memory-node.vue';
import RetrievalNode from './components/nodes/retrieval-node.vue';
import WriteNode from './components/nodes/write-node.vue';
import HttprequestNode from './components/nodes/httprequest-node.vue';
import QANode from './components/nodes/QA-node.vue';
import Workflow from './components/nodes/workflow-node.vue';
import PluginNode from './components/nodes/plugin-node.vue';
import DatabaseNode from './components/nodes/database-node.vue';
import DatabaseInsertNode from './components/nodes/database-insert-node.vue';
import DatabaseUpdateNode from './components/nodes/database-update-node.vue';
import DatabaseSelectNode from './components/nodes/database-select-node.vue';
import DatabaseDeleteNode from './components/nodes/database-delete-node.vue';

const emit = defineEmits([]);
const elements = ref();
const { getAllNode, getModelList, setDataObj, getDataobj } = flowStore();

const props = defineProps({
  id: {
    type: String,
    default: ''
  }
})
// 配置节点类型
const nodeTypes = {
  startcode: markRaw(StartNode),
  endcode: markRaw(EndNode),
  inputcode: markRaw(InputNode),
  outputcode: markRaw(OutputNode),
  code: markRaw(CodeNode),
  aimodel: markRaw(LLMNode),
  selectorcode: markRaw(ConditionNode),
  createSession: markRaw(DiscussionNode),
  updateSession: markRaw(EditDscussionNode),
  deleteSession: markRaw(DelDscussionNode),
  selectSession: markRaw(CheckDscussionNode),
  clearHistorySession: markRaw(ContextNode),
  selectHistorySession: markRaw(CheckContextNode),
  selectMessage: markRaw(QueueNode),
  createMessage: markRaw(CreateQueueNode),
  updateMessage: markRaw(EditQueueNode),
  deleteMessage: markRaw(DelQueueNode),
  variableAssignment: markRaw(AssignmentNode),
  creditPeriodMemory: markRaw(MemorytNode),
  knowledgeBaseRetrieval: markRaw(RetrievalNode),
  knowledgeBaseWriting: markRaw(WriteNode),
  HTTPRequest: markRaw(HttprequestNode),
  IntentionRecognition: markRaw(IntentNode), 
  circulation: markRaw(LoopNode), 
  batchprocessing: markRaw(BatchNode), 
  questions: markRaw(QANode),
  flowcode: markRaw(Workflow),
  variableAggregation: markRaw(VariableMergeNode),
  customizeSQL: markRaw(DatabaseNode),
  insertdata: markRaw(DatabaseInsertNode),
  updatedata: markRaw(DatabaseUpdateNode),
  selectdata: markRaw(DatabaseSelectNode),
  deteledata: markRaw(DatabaseDeleteNode),
  plugincode: markRaw(PluginNode),
};

const edgeTypes = {
  custom: markRaw(CustomEdge),
};
const {
  addNodes,
  addEdges,
  updateNodeData,
  onConnect,
  onEdgesChange,
  onNodesChange
} = useVueFlow();

// 连接节点
onConnect(async (params) => {
  const { source, target, sourceHandle } = params;
  // addEdges({ ...params, id: `${source}->${target}->${sourceHandle|| ''}`, type: 'custom' });

  const node = getDataobj()[source];
  const selectionlist = node?.selectionlist || [];
  const newNode = {
    sourceid: source,
    targetid: target,
    name: `${source}->${target}->${sourceHandle || ''}`
  }
  const isIncludes = selectionlist.findIndex(item => item.name == newNode.name);
  console.log(isIncludes)
  if (isIncludes >= 0) return;
  selectionlist.push(newNode)
  try {
    await editEdges({ data:{ id: source, nodecode: node?.nodecode, selectionlist}});
    updateNodeData(source, {selectionlist})
    addEdges({ ...params, id: `${source}->${target}->${sourceHandle|| ''}`, type: 'custom' });
  }finally{}
});


// 保存节点位置
const handleNodeDragStop = async({ node }) => {
  const { id, position} = node;
  await editPosition({ data:{ id, ...position }})
}
// 新增节点
const popRef = ref(null);
const handleAddNodes = async (nodeInfo, flowserviceid=undefined, plugin) => {
  const nodeid = nodeInfo.id;
  try {
    const { data } = await createNode({data: {serviceaiid: props.id, nodeid,flowserviceid, x: 100, y: 200}})
    const newNode = {
      id: data.id,
      position: { x: 100, y: 200 },
      type: data.nodecode,
      label: data.nodename,
      data: {
        ...data,
        ...plugin
      }
    }
    addNodes([newNode])
    setDataObj(data);
  }finally {
    popRef.value?.hide();
  }
}
const handleHidePanel = () => {
  popRef.value?.hide();
}
// 监听节点删除
onNodesChange(async (changes) => {
  for (const change of changes) {
    if (change.type == 'remove') {
      try {
        const { id } = change;
        await deleteNode({
          data: { id },
        });
      }finally{}
    }
  }
})
// 监听连线删除
onEdgesChange(async (changes) => {
  for (const change of changes) {
    if (change.type == 'remove') {
      const { id, source } = change;
      const node = getDataobj()[source];
      const selectionlist = node.selectionlist || [];
      const index = selectionlist?.findIndex(selection => selection.name == id);
      selectionlist.splice(index, 1)
      try {
        await editEdges({ data:{ id: source, nodecode: node.nodecode, selectionlist}});
        updateNodeData(source, {selectionlist})
      }finally{}
    } else if (change.type == 'add') {
      // const { source, target, sourceHandle } = change.item;
      // console.log(change.type, change.item)
      // const node = getDataobj()[source];
      // const selectionlist = node?.selectionlist || [];
      // selectionlist.push({
      //   sourceid: source,
      //   targetid: target,
      //   name: `${source}->${target}->${sourceHandle || ''}`
      // })
      // try {
      //   await editEdges({ data:{ id: source, nodecode: node?.nodecode, selectionlist}});
      //   updateNodeData(source, {selectionlist})
      // }finally{}
    }
  }
});

// 转换成VueFlow可用的Edge数据
const transToEdges = (item) => {
  const temp = [];
  if (item.selectionlist && item.selectionlist.length) {
    item.selectionlist.forEach(selection => {
      const sourceHandle = selection.name.split('->')[2];
      temp.push({
        id: selection.name, 
        target: selection.targetid,
        source: selection.sourceid,
        sourceHandle,
        type: 'custom' 
      })
    })
  }
  return temp;
}

const init = async (id) => {
  if (!id) return;
  try {
    getAllNode();
    await initFlow({ data: { id } });
    const { data } = await initFlowDetail({ data: { serviceaiid: id } });
    const edgeData = [];
    data.forEach(item => {
      const deletable = !(item.nodecode == 'startcode' || item.nodecode == 'endcode');
      const positionX = item.nodecode == 'endcode' ? 500 : 25;
      const newNode = {
          id: item.id,
          position: { x: Number(item.x) || positionX, y: Number(item.y) || 100 },
          type: item.nodecode,
          deletable: deletable,
          label: item.nodename,
          data: {
            ...item
          }
        }
        addNodes([newNode])
        const edgeInfo = transToEdges(item)
        edgeData.push(...edgeInfo)
    })
    addEdges(edgeData)
    setDataObj(data);
    getModelList()
  }finally{}
}

defineExpose({
  init
})
</script>

<style lang="scss">
@import '@vue-flow/core/dist/style.css';
@import '@vue-flow/core/dist/theme-default.css';
@import '@vue-flow/controls/dist/style.css';
@import './style/custom.scss';

.flow-footer {
  position: fixed;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  background-color: #fff;
  padding: 4px 10px;
  border-radius: 6px;
}

#main-canvas {
  --vf-handle: #4d53e8;

  .vue-flow__handle {
    width: 16px;
    height: 16px;
    transform: scale(0.5);
    transition: all 0.2s linear 0s;
    border: 4px solid #fff;
    box-sizing: content-box;
  }
  .vue-flow__handle-left {
    translate: -50% -50%;
  }
  .vue-flow__handle-right {
    translate: 50% -50%;
  }
  .vue-flow__node.selectable:focus,
  .vue-flow__node.selectable:focus-visible {
    outline: 1px solid #4d53e8;
    border-radius: 8px;
  }
  .vue-flow__node {
    cursor: pointer;
  }
  .vue-flow__node:hover {
    .vue-flow__handle {
      transform: scale(1);
    }
  }
  .vue-flow__edge {
    transform: translate(-7px, -7px);
    pointer-events: visible;
    cursor: grab;
  }

  .ivu-form-item-error-tip {
    font-size: 12px;
    padding-top: 0;
  }
  .vue-flow__node-input {
  }
}
</style>
